#pragma once

#include <algorithm/analytical_model/algorithm/cuda/math/vector/Common.cuh>
#include <algorithm/shared/utils/GenericVectorMath.h>
#include <utils/config.h>

namespace solar::cuda
{
    using uint = unsigned int;
}

inline HOT_FUNCTION __host__ __device__ auto make_uint3(int s) -> uint3
{
    return ::make_uint3(s, s, s);
}
inline HOT_FUNCTION __host__ __device__ auto make_uint3(const float3& a) -> uint3
{
    return ::make_uint3(solar::cuda::uint(a.x), solar::cuda::uint(a.y), solar::cuda::uint(a.z));
}

inline HOT_FUNCTION __host__ __device__ auto operator-(const uint3& a) -> uint3
{
    return solar::internal::vector::negate(a);
}

inline HOT_FUNCTION __host__ __device__ auto operator+(const uint3& a, const uint3& b) -> uint3
{
    return solar::internal::vector::plus(a, b);
}

inline HOT_FUNCTION __host__ __device__ auto operator+(const uint3& a, const solar::cuda::uint& b) -> uint3
{
    return solar::internal::vector::plus(a, b);
}

inline HOT_FUNCTION __host__ __device__ auto operator+(const solar::cuda::uint& b, const uint3& a) -> uint3
{
    return solar::internal::vector::plus(a, b);
}

inline HOT_FUNCTION __host__ __device__ void operator+=(uint3& a, const uint3& b)
{
    solar::internal::vector::plusSelf(a, b);
}

inline HOT_FUNCTION __host__ __device__ auto operator-(const uint3& a, const uint3& b) -> uint3
{
    return solar::internal::vector::minus(a, b);
}

inline HOT_FUNCTION __host__ __device__ auto operator-(const uint3& a, const solar::cuda::uint& b) -> uint3
{
    return solar::internal::vector::minus(a, b);
}

inline HOT_FUNCTION __host__ __device__ auto operator-(const solar::cuda::uint& b, const uint3& a) -> uint3
{
    return solar::internal::vector::minus(a, b);
}

inline HOT_FUNCTION __host__ __device__ void operator-=(uint3& a, const uint3& b)
{
    solar::internal::vector::minusSelf(a, b);
}

inline HOT_FUNCTION __host__ __device__ auto operator*(const uint3& a, const uint3& b) -> uint3
{
    return solar::internal::vector::multiply(a, b);
}

inline HOT_FUNCTION __host__ __device__ auto operator*(const uint3& a, const float&& s) -> uint3
{
    return solar::internal::vector::multiply(a, s);
}

inline HOT_FUNCTION __host__ __device__ auto operator*(const solar::cuda::uint& s, const uint3& a) -> uint3
{
    return solar::internal::vector::multiply(a, s);
}

inline HOT_FUNCTION __host__ __device__ void operator*=(uint3& a, const solar::cuda::uint& s)
{
    solar::internal::vector::multiplySelf(a, s);
}

inline HOT_FUNCTION __host__ __device__ auto operator/(const uint3& a, const uint3& b) -> uint3
{
#ifdef __CUDA_ARCH__
    return solar::internal::vector::cuda::divide(a, b);
#else
    return solar::internal::vector::divide(a, b);
#endif
}

inline HOT_FUNCTION __host__ __device__ auto operator/(const uint3& a, const solar::cuda::uint& s) -> uint3
{
#ifdef __CUDA_ARCH__
    return solar::internal::vector::cuda::divide(a, s);
#else
    return solar::internal::vector::divide(a, s);
#endif
}

inline HOT_FUNCTION __host__ __device__ auto operator/(const solar::cuda::uint& s, const uint3& a) -> uint3
{
#ifdef __CUDA_ARCH__
    return solar::internal::vector::cuda::divide(a, s);
#else
    return solar::internal::vector::divide(a, s);
#endif
}

inline HOT_FUNCTION __host__ __device__ void operator/=(uint3& a, const solar::cuda::uint& s)
{
#ifdef __CUDA_ARCH__
    solar::internal::vector::cuda::divideSelf(a, s);
#else
    solar::internal::vector::divideSelf(a, s);
#endif
}

inline HOT_FUNCTION __device__ __host__ auto lerp(const uint3& a, const uint3& b,
                                                  const solar::cuda::uint& t) -> uint3
{
    return solar::internal::vector::lerp(a, b, t);
}

inline HOT_FUNCTION __device__ __host__ auto clamp(const uint3& v, const solar::cuda::uint& a,
                                                   const solar::cuda::uint& b) -> uint3
{
    return solar::internal::vector::clamp(v, a, b);
}

inline HOT_FUNCTION __device__ __host__ auto clamp(const uint3& v, const uint3& a,
                                                   const uint3& b) -> uint3
{
    return solar::internal::vector::clamp(v, a, b);
}

inline HOT_FUNCTION __host__ __device__ auto dot(const uint3& a, const uint3& b) -> int
{
    return solar::internal::vector::dot(a, b);
}

inline HOT_FUNCTION __host__ __device__ auto length(const uint3& v) -> float
{
    return solar::internal::vector::length(v);
}

inline HOT_FUNCTION __host__ __device__ auto rlength(const uint3& v) -> float
{
    return solar::internal::vector::rlength(v);
}

inline HOT_FUNCTION __host__ __device__ auto normalize(const uint3& v) -> uint3
{
    return solar::internal::vector::normalize(v);
}

inline HOT_FUNCTION __host__ __device__ auto floor(const uint3& v) -> uint3
{
    return solar::internal::vector::floor(v);
}

inline HOT_FUNCTION __host__ __device__ auto reflect(const uint3& i, const uint3& n) -> uint3
{
    return solar::internal::vector::reflect(i, n);
}

inline HOT_FUNCTION __host__ __device__ auto abs(const uint3& v) -> uint3
{
    return solar::internal::vector::abs(v);
}

inline HOT_FUNCTION __host__ __device__ auto max(const uint3& a, const uint3& b) -> uint3
{
    return solar::internal::vector::max(a, b);
}

inline HOT_FUNCTION __host__ __device__ auto min(const uint3& a, const uint3& b) -> uint3
{
    return solar::internal::vector::min(a, b);
}

inline HOT_FUNCTION __host__ __device__ auto cross(const uint3& a, const uint3& b) -> uint3
{
    return ::make_uint3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
}
